/*
 * p2spSrvr.cc
 *
 *  Created on: 2009-10-31
 *      Author: root
 */

#include "p2sp.h"
#include <random.h>

static class p2spSrvrAppClass : public TclClass {
  public:
      p2spSrvrAppClass() : TclClass("Application/p2sp/p2spSrvr") {}
      TclObject* create(int, const char*const*) {
        return (new p2spSrvrApp);
      }
} class_p2sp_srvr;

//Constructor

p2spSrvrApp::p2spSrvrApp() : running_(0), seqno_(0), dbgno_(0)
{
	  bind("chunksize_", &chunksize_);
	  bind("filesize_", &filesize_);
}

int
p2spSrvrApp::command(int argc, const char*const* argv) {
	  Tcl& tcl = Tcl::instance();

	  if(argc == 3) {
		  if (strcmp(argv[1], "attach-agent") == 0) {
			  agent_ = (Agent*) TclObject::lookup(argv[2]);
			  if (agent_ == 0) {
				tcl.resultf("no such agent %s", argv[2]);
				return(TCL_ERROR);
			   }
		  }

	  agent_->attachApp(this);
	  return(TCL_OK);
	  }

	  return (Application::command(argc, argv));

}

void
p2spSrvrApp::start() {
	  int nChunks = filesize_/chunksize_ + (filesize_%chunksize_? 1:0);

	  chkmap_size_ = nChunks/8 + (nChunks%8 ? 1:0);
	  assert(chkmap_size_ < MAX_CHKMAP);

	  fprintf(stderr, ""TIME_FORMAT"\t Node %d Start p2sp App Server\n", CURRENT_TIME, agent_->addr());
	  running_ = 1;
}

void
p2spSrvrApp::stop() {
	  fprintf(stderr, ""TIME_FORMAT"\t Node %d Stop p2sp App Server\n", CURRENT_TIME, agent_->addr());
	  running_ = 0;
}

void
p2spSrvrApp::recv_msg(int nbytes, const char *msg) {

}

void
p2spSrvrApp::process_data(int size, AppData* data) {
	  if(data) {
		  if(running_){
				//message process in p2sp Server
				//p2spAppData* p2sp_data = (p2spAppData*) data;
				  PacketData* p2sp_pkt_data = (PacketData*) data;
				  p2sp_data_t* p2sp_data = (p2sp_data_t*)(p2sp_pkt_data->data());
				  assert(p2sp_data->type_ == SRVR_REQUEST || p2sp_data->type_ == IS_RECVING);
				  if (p2sp_data->type_ == SRVR_REQUEST){
						int tx_chunkid = p2sp_data->chunk_id_;
						assert(tx_chunkid < chkmap_size_ * 8);
						send_p2sp_pkt(p2sp_data->src_id_, tx_chunkid);
				  }
				  //delete data;
			}

	  }
}

void
p2spSrvrApp::send_p2sp_pkt(nsaddr_t dst, int chunkid){


  if (running_){
    //data can be sent only if server is in running state
    //p2spAppData* p2sp_data = new p2spAppData();

    PacketData* p2sp_pkt_data = new PacketData(sizeof(p2sp_data_t) + chunksize_);
    p2sp_data_t* p2sp_data = (p2sp_data_t*)(p2sp_pkt_data->data());

    p2sp_data->type_ = SRVR_DATA;
    p2sp_data->chunk_id_ = chunkid; //change 1 to one choose chunk from request bitmap
    p2sp_data->nbytes_ = sizeof(p2sp_data_t) + chunksize_; // size of p2sp application data packet, size is equal to chunk size
    p2sp_data->src_id_ = agent_->addr();
    p2sp_data->dst_id_ = dst;
    p2sp_data->seqno_ = ++seqno_;

//    delete p2sp_data;
//    PacketData* pdata = new PacketData(p2sp_data->nbytes);
//    memcpy(pdata->data(), (unsigned char*)p2sp_data->type_, p2sp_data->nbytes);

 //   printf("Server %d send chunk %d\n", agent_->addr(), chunkid);
    agent_->sendto(p2sp_data->nbytes_, p2sp_pkt_data, 0, dst);


  }

}

/*int
p2spSrvrApp::select_chunk(unsigned char *req_bmp) {
  //return 0 based index of the chunks
  uint16_t chunkid = 0;
  uint16_t chunknum = 0;
  int num_chunks = 0;
  int i,j, retid;;
  //printf("SEL:");
  //for(i = 0; i< nChunks_; i++)
  //    printf("%02X ", req_bmp[i]);
  //printf("\n");

  for(i = 0; i < nChunks_; i++ ) {
      for(j = 0; j < 8; j++)
          num_chunks += (req_bmp[i] >> j) & 1;
  }

  if(num_chunks == 0)
      return -1;

  chunknum = Random::integer(num_chunks) + 1;

  for(i = 0; i < nChunks_; i++) {
        for(j = 0; j < 8; j++) {
            chunknum -= (req_bmp[i] >> j) & 1;
            chunkid ++;
            if(chunknum == 0)
                return chunkid - 1;
        }
  }

    uint16_t chunkid = 0;
    uint16_t chunknum = 0;
    int num_chunks = 0;
    int i,j, retid;
    int minpending = 64;

    for(i = 0; i < nChunks_; i++ ) {
        for(j = 0; j < 8; j++)
            if ((req_bmp[i] >> j) & 1){
                num_chunks ++;
                retid = i*8 +j;
                if(tx_Pending_[retid] < minpending){
                        minpending = tx_Pending_[retid];
                        chunkid =retid;
                }
            }
    }

    if(num_chunks == 0)
        return -1;
    return chunkid;

}*/
